﻿using System;
using System.IO;
using System.Linq;
using System.Windows;
using AvalonDock;
using Caliburn.Micro;
using OPSYS_Schematic.UIInfrastructure;

namespace OPSYS_Schematic.Presentation.Infrastructure
{
    public interface IDockWindowManager : IWindowManager
    {
        void Initialize();

        void Show(IDockScreen screen, DockType? dockType = null, bool activate = true, DockSide dockSide = DockSide.Left);

        void Hide(IDockScreen screen);

        void Close(IDockScreen screen);

        void Save(string path);

        void Restore(string path);
    }

    public class AvalonDockWindowManager : WindowManager, IDockWindowManager
    {
        #region Field

        private DockingManager _dockingManager;
        private bool isLoaded;

        #endregion

        #region Constructor

        #endregion

        #region Method

        private DockableContent GetDockable(IDockScreen rootModel)
        {
            //see if the dockable exists
            DockableContent existingView =
                _dockingManager.DockableContents.FirstOrDefault(dc => dc.DataContext == rootModel);
            if (existingView != null)
                return existingView;

            //var view = ViewLocator.LocateForModel(rootModel, null, DockType.DockableContent);
            UIElement view = ViewLocator.LocateForModel(rootModel, null,
                                                        new object[] {DockType.DockableContent, rootModel});
            ViewModelBinder.Bind(rootModel, view, DockType.DockableContent);

            return view as DockableContent;
        }

        private DocumentContent GetDocument(IDockScreen rootModel)
        {
            //see if the dockable exists
            DocumentContent existingView = _dockingManager.Documents.FirstOrDefault(dc => dc.DataContext == rootModel);
            if (existingView != null)
                return existingView;

            //var view = ViewLocator.LocateForModel(rootModel, null, DockType.Document);
            UIElement view = ViewLocator.LocateForModel(rootModel, null, new object[] {DockType.Document, rootModel});
            ViewModelBinder.Bind(rootModel, view, DockType.Document);

            return view as DocumentContent;
        }

        private DockingManager GetDockingManager(Window window = null)
        {
            DockingManager dockingManager = _dockingManager;

            if (dockingManager == null && Application.Current.MainWindow != null)
            {
                dockingManager = Application.Current.MainWindow.FindChild<DockingManager>();
                _dockingManager = dockingManager;
            }

            if (dockingManager == null)
                throw new InvalidOperationException("Unable to retrieve a DockingManager ");

            return dockingManager;
        }

        private static AnchorStyle GetAnchorStyle(DockSide side)
        {
            switch (side)
            {
                case DockSide.None:
                    return AnchorStyle.None;
                case DockSide.Left:
                    return AnchorStyle.Left;
                case DockSide.Top:
                    return AnchorStyle.Top;
                case DockSide.Right:
                    return AnchorStyle.Right;
                case DockSide.Bottom:
                    return AnchorStyle.Bottom;
                default:
                    return AnchorStyle.Left;
            }
        }

        #endregion

        private string layoutPath = string.Empty;

        public bool IsLoaded
        {
            get { return isLoaded; }
        }

        internal ManagedContent this[string name]
        {
            get
            {
                if (!IsLoaded)
                {
                    throw new Exception("The DockWindowManager has not been initialize, please init it first.");
                }

                DockableContent dockContent =
                    _dockingManager.DockableContents.FirstOrDefault(item => string.Equals(item.Name, name));
                if (dockContent == null)
                {
                    return _dockingManager.Documents.FirstOrDefault(item => string.Equals(item.Name, name));
                }
                return dockContent;
            }
        }

        #region IDockWindowManager Members

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        public void Initialize()
        {
            _dockingManager = GetDockingManager();
            isLoaded = true;
        }

        public void Show(IDockScreen screen, DockType? dockType, bool activate, DockSide dockSide)
        {
            DockType type = dockType.HasValue ? dockType.Value : screen.Type;
            switch (type)
            {
                case DockType.Document:
                    ShowDocumentWindow(screen, activate);
                    break;
                case DockType.DockableContent:
                    ShowDockedWindow(screen, activate, dockSide);
                    break;
                case DockType.Floating:
                    ShowFloatingWindow(screen, activate);
                    break;
                default:
                    break;
            }
        }

        public void Hide(IDockScreen screen)
        {
            ManagedContent content = this[screen.Name];
            if (content != null)
            {
                content.Hide();
            }
        }

        public void Close(IDockScreen screen)
        {
            ManagedContent content = this[screen.Name];
            if (content != null)
            {
                content.Close();
            }
        }

        public void Save(string path)
        {
            _dockingManager.SaveLayout(path);
        }

        public void Restore(string path)
        {
            if (!File.Exists(path))
            {
                return;
            }

            layoutPath = path;
            if (_dockingManager != null)
            {
                _dockingManager.Loaded += manager_Loaded;
            }
        }

        #endregion

        private void ShowDockedWindow(IDockScreen viewModel,
                                      bool activate = true,
                                      DockSide dockSide = DockSide.Left)
        {
            DockableContent dockableContent = GetDockable(viewModel);
            dockableContent.Show(_dockingManager, GetAnchorStyle(dockSide));
            if (activate)
                dockableContent.Activate();
            else
                dockableContent.ToggleAutoHide();
        }

        private void ShowFloatingWindow(IDockScreen viewModel, bool activate = true)
        {
            DockableContent dockableContent = GetDockable(viewModel);
            dockableContent.ShowAsFloatingWindow(_dockingManager, true);
            if (activate)
                dockableContent.Activate();
        }

        private void ShowDocumentWindow(IDockScreen viewModel, bool activate = true)
        {
            DocumentContent document = GetDocument(viewModel);
            document.Show(_dockingManager);
            if (activate)
                document.Activate();
        }

        private void manager_Loaded(object sender, RoutedEventArgs e)
        {
            var manager = sender as DockingManager;
            manager.Loaded -= manager_Loaded;
            manager.RestoreLayout(layoutPath);
        }
    }

    public class DockWindowLoadedMessage : IMessage
    {
    }
}